package cz.spock.core.service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.google.appengine.api.datastore.Email;

import cz.spock.core.entity.Account;
import cz.spock.core.entity.registration.Registration;
import cz.spock.core.entity.security.AccountSecurity;
import cz.spock.exception.EmailException;
import cz.spock.exception.InvalidInputException;


/**
 * provides utilities used by other service classes
 * 
 * @author miso
 *
 */
@Component
public class UtilService extends BaseService {
    
    private ShaPasswordEncoder passwordEncoder;
    private AuthenticationManager authenticationManager;
    
    
    // spring injection by setter
    public void setPasswordEncoder(ShaPasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }
    
    
    /**
     * creates hash using org.springframework.security.authentication.encoding.ShaPasswordEncoder
     * 
     * @param text
     * @param salt
     * @return
     */
    public String shaHash(String text, String salt) {
        return passwordEncoder.encodePassword(text, salt);
    }
    

    /**
     * sends registration email
     * 
     * @param email
     * @param link
     */
    public boolean sendRegMail(String email, String link) {
        String message = "Visit this page to continue in registration: ";
        String fullLink = "http://localhost:8888/registration/" + link;
        
        return sendMail(email, "mknazsky@gmail.com", message + fullLink, "Registration info"); 
    }

    /**
     * sends email
     * 
     * @param to - email of recipient
     * @param from - email of sender (must match GAE's rules)
     * @param message
     * @param subject
     * @return true if email was sent without trouble
     */
    public boolean sendMail(String to, String from, String message, String subject) {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        try {
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress(from));
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
            msg.setSubject(subject);
            msg.setText(message);
            Transport.send(msg);
            System.out.println("Email sent");
        } catch (AddressException e) {
            throw new EmailException("EmailException caused by AdressException: " + e.getMessage());
        } catch (MessagingException e) {
            throw new EmailException("EmailException caused by MessagingException: " + e.getMessage());
        }
        return true;
    }
    
    
    /**
     * searches whether email is already used (is in database of accounts or registration requests)
     * 
     * @param email
     * @return true only if email isn't used in database
     */
    public boolean isEmailUnique(Email email) {        
        if(dao.ofy().query(Account.class).filter("email", email).get() != null)
            return false;
        if(dao.ofy().query(Registration.class).filter("email", email).get() != null)
            return false;
        return true;
    }
    
    /**
     * creates date object from given string and date format
     * - if format is null, default is used - dd/MM/yyyy
     * - if date can not be parsed InvalidInputException is thrown
     * 
     * @param date
     * @param format
     * @return
     */
    public Date parseDate(String date, String format) {
        if(format == null) {
            format = "dd/MM/yyyy";
        }
        SimpleDateFormat df = new SimpleDateFormat(format);
        Date parsed = null;
        try {
            parsed = df.parse(date);
        } catch (ParseException e) {
            throw new InvalidInputException("Date " + date + " can not be parsed.", 410);
        }
        return parsed;
    }
    

    
    /**
     * logins user to application - spring security
     * 
     * @param login
     * @param pass
     */
    public boolean loginUser(String login, String pass) {
        try {            
            UserDetails accsec = dao.ofy().query(AccountSecurity.class).filter("login", login).get();
            if (accsec == null) {
                return false;
            }
            UsernamePasswordAuthenticationToken usernameAndPassword = new UsernamePasswordAuthenticationToken(
                    login, pass, accsec.getAuthorities());

            Authentication auth = authenticationManager.authenticate(usernameAndPassword);
            SecurityContextHolder.getContext().setAuthentication(auth);
        } catch (AuthenticationException e) {
            SecurityContextHolder.getContext().setAuthentication(null);
            log.severe("AuthenticationException in method loginUser: " + e.getMessage());
            return false;
        }
        return true;
    }


    
}
